package cn.keniykey.mc.viewlibrary.channel;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.bukkit.entity.Player;

import cn.keniykey.mc.viewlibrary.reflection.ReflectionClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionField;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionGenericClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionMethod;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionUtil;
import cn.keniykey.mc.viewlibrary.reflection.SearchReflectionMethod;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import net.minecraft.server.level.EntityPlayer;

public final class ChannelUtilV1_17_1 extends AbChannelUtil{
	private final ReflectionMethod method_bukkit_craftplayer_gethandle;
	
	private final ReflectionField field_nms_entityplayer_playerconnection;
	
	private final ReflectionMethod method_nms_playerconnection_sendpacket;
	private final ReflectionField field_nms_playerconnection_networkmanager;
	
	private final ReflectionField field_nms_networkmanager_channel;
	
	private final ExecutorService service_add = Executors.newSingleThreadExecutor();
	private final ExecutorService service_remove = Executors.newSingleThreadExecutor();
	
	ChannelUtilV1_17_1() {
		this.method_bukkit_craftplayer_gethandle = ReflectionUtil.findReflectionClass("cb,entity.CraftPlayer").getMethod("getHandle");

		final ReflectionClass clazz_playerconnection = ReflectionUtil.findReflectionClass("nm,server.network.PlayerConnection");
		ReflectionClass clazz_packet = ReflectionUtil.findReflectionClass("nm,network.protocol.Packet");
		this.field_nms_entityplayer_playerconnection = ReflectionUtil.findReflectionClass("nm,server.level.EntityPlayer").findField(clazz_playerconnection);
		final ReflectionGenericClass genclazz_packet = new ReflectionGenericClass(clazz_packet.getClazz()).addWildcard();
		this.method_nms_playerconnection_sendpacket = clazz_playerconnection.findMethod(new SearchReflectionMethod()
				.setName("sendPacket").addParameter(genclazz_packet), new SearchReflectionMethod()
				.setName("a").addParameter(genclazz_packet));
		final ReflectionClass clazz_networkmanager = ReflectionUtil.findReflectionClass("nm,network.NetworkManager");
		this.field_nms_playerconnection_networkmanager = clazz_playerconnection.findField(clazz_networkmanager);
		this.field_nms_networkmanager_channel = clazz_networkmanager.findField(Channel.class);
	}

	@Override
	public void addPlayerAbPacketReceiveHandler(Player player, AbPacketReceiveHandler handler) {
		handler.setPlayer(player);
		this.service_add.submit( ()-> {
			final Channel channel = this.getPlayerChannel(player);
			if(channel.pipeline().get(CHANNEL_NAME) != null) channel.pipeline().remove(CHANNEL_NAME);
			channel.pipeline().addBefore(CHANNEL_MAIN, CHANNEL_NAME, new PacketChannelHandler(handler));
		});
	}
	
	private final class PacketChannelHandler extends ChannelDuplexHandler{
		private final AbPacketReceiveHandler handler;
		PacketChannelHandler(AbPacketReceiveHandler handler1) {
			this.handler = handler1;
		}
		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			if(this.handler.onServerReceivePacket(msg)) return;
			super.channelRead(ctx, msg);
		}
	}
	
	@Override
	public void removePlayerAbPacketReceiveHandler(Player player) {
		this.service_remove.submit( () -> {
			final Channel channel = this.getPlayerChannel(player);
			if(channel.pipeline().get(CHANNEL_NAME) != null) channel.pipeline().remove(CHANNEL_NAME);
		});
	}

	@Override
	public void sendPacket(Player player, Object packet) {
		final EntityPlayer nms_player = (EntityPlayer) this.method_bukkit_craftplayer_gethandle.dynamicInvoke(player);
		final Object playercon = this.field_nms_entityplayer_playerconnection.dynamicGet(nms_player);
		this.method_nms_playerconnection_sendpacket.dynamicInvoke(playercon, packet);
//		nms_player.b.sendPacket((Packet<?>)packet); //1,17
//      nms_player.b.a((Packet<?>)packet); //1.18
	}
	
	private Channel getPlayerChannel(Player player) {
		final EntityPlayer nms_player = (EntityPlayer) this.method_bukkit_craftplayer_gethandle.dynamicInvoke(player);
        //PlayerConnection.NetworkManager.Channel
//		return nms_player.networkManager.k; //仅PaperSpigot有用
//		return nms_player.b.a.k; //1.17
//      return nms_player.b.a.m; //1.18
		final Object playercon = this.field_nms_entityplayer_playerconnection.dynamicGet(nms_player);
		final Object network = this.field_nms_playerconnection_networkmanager.dynamicGet(playercon);
		return (Channel) this.field_nms_networkmanager_channel.dynamicGet(network);
	}
	
}